home *** CD-ROM | disk | FTP | other *** search
/ AGA Toolkit '97 / The AGA Toolkit '97.iso / miscellaneous / science / maths / calc / source / input.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-07  |  14.9 KB  |  684 lines

  1. /*
  2.  * Copyright (c) 1994 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * Modified for the Amiga by Steve Leblanc, Oct 1995
  7.  *
  8.  * Nested input source file reader.
  9.  * For terminal input, this also provides a simple command stack.
  10.  */
  11.  
  12. #include <ctype.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include "calc.h"
  16. #include "config.h"
  17.  
  18. extern int stdin_tty;        /* TRUE if stdin is a tty */
  19.  
  20. #define TTYSIZE        100    /* reallocation size for terminal buffers */
  21. #define DEPTH        10    /* maximum depth of input */
  22. #define IS_READ        1    /* reading normally */
  23. #define IS_REREAD    2    /* reread current character */
  24. #define chartoint(ch)    ((ch) & 0xff)    /* make sure char is not negative */
  25. #define READSET_ALLOC    8    /* readset to allocate chunk size */
  26.  
  27.  
  28. typedef struct {
  29.     int i_state;        /* state (read, reread) */
  30.     int i_char;        /* currently read char */
  31.     long i_line;        /* line number */
  32.     char *i_str;        /* current string for input (if not NULL) */
  33.     char *i_origstr;    /* original string so it can be freed */
  34.     char *i_ttystr;        /* current character of tty line (or NULL) */
  35.     FILE *i_fp;        /* current file for input (if not NULL) */
  36.     char *i_name;        /* file name if known */
  37. } INPUT;
  38.  
  39.  
  40. /* files that calc has read or included */
  41. typedef struct {
  42.     int active;        /* != 0 => active entry, 0 => unused entry */
  43.     char *name;        /* name used to read file */
  44.     char *path;        /* real path used to open file */
  45.     struct stat inode;    /* inode information for file */
  46. } READSET;
  47.  
  48. static READSET *readset = NULL;        /* array of files read */
  49. static int maxreadset = 0;        /* length of readset */
  50.  
  51. static int linesize;        /* current max size of input line */
  52. static char *linebuf;        /* current input line buffer */
  53. static char *prompt;        /* current prompt for terminal */
  54. static BOOL noprompt;        /* TRUE if should not print prompt */
  55.  
  56. static int depth;        /* current input depth */
  57. static INPUT *cip;        /* current input source */
  58. static INPUT inputs[DEPTH];    /* input sources */
  59.  
  60. static int openfile MATH_PROTO((char *name));
  61. static int ttychar MATH_PROTO((void));
  62. static void closeinput MATH_PROTO((void));
  63. static int isinoderead MATH_PROTO((struct stat *sbuf));
  64. static int findfreeread MATH_PROTO((void));
  65. static int addreadset MATH_PROTO((char *name, char *path, struct stat *sbuf));
  66.  
  67. /*
  68.  * Read a line into the specified buffer.  The line ends in a newline,
  69.  * and is NULL terminated.  Returns the number of characters read, or
  70.  * zero on an end of file or error.  The prompt is printed before reading
  71.  * the line.
  72.  *
  73.  * AMIGA: This routine taken out of the hist.c file and modified.
  74.  *        The Amiga version relies on the Console handler for history
  75.  *        and line editing (i.e. KingCON) so stuff in hist.c was not
  76.  *        required.
  77.  */
  78. int
  79. getline(char *prompt,char *buf,int len)
  80. {
  81.     fputs(prompt,stdout);
  82.     fflush(stdout);
  83.  
  84.     if (fgets(buf, len, stdin) == NULL)
  85.         return 0;
  86.     return strlen(buf);
  87. }
  88.  
  89. /*
  90.  * Open an input file by possibly searching through a path list
  91.  * and also possibly applying the specified extension.  For example:
  92.  * opensearchfile("barf", ".:/tmp", ".c") searches in order for the
  93.  * files "./barf", "./barf.c", "/tmp/barf", and "/tmp/barf.c".
  94.  *
  95.  * Returns -1 if we could not open a file or error.  
  96.  * Returns 1 if file was opened and added to/updated in the readset.   
  97.  * Returns 0 if file was already in the readset and reopen was 0.
  98.  */
  99. int
  100. opensearchfile(name, pathlist, extension, rd_once)
  101.     char *name;        /* file name to be read */
  102.     char *pathlist;        /* list of colon separated paths (or NULL) */
  103.     char *extension;    /* extra extension to try (or NULL) */
  104.     int rd_once;        /* TRUE => do not reread a file */
  105. {
  106.     int i;
  107.     char *cp;
  108.     char path[PATHSIZE+1];    /* name being searched for */
  109.     struct stat statbuf;    /* stat of the path */
  110.  
  111.     /*
  112.      * Don't try the extension if the filename already contains it.
  113.      */
  114.     if (extension) {
  115.         i = strlen(name) - strlen(extension);
  116.         if ((i >= 0) && (strcmp(&name[i], extension) == 0))
  117.             extension = NULL;
  118.     }
  119.     /*
  120.      * If the name is absolute, or if there is no path list, then
  121.      * make one which just searches for the name straight.  Then
  122.      * search through the path list for the file, without and with
  123.      * the specified extension.
  124.    *
  125.    * AMIGA: Modified to recognize a full path as one that looks like
  126.    *          XXX:qwerty/uiops
  127.    */
  128.     if(strchr(name,':') || pathlist == NULL)
  129.         pathlist = "";
  130.  
  131.   pathlist--;
  132.     do {
  133.         pathlist++;
  134.         cp = path;
  135.         while (*pathlist && (*pathlist != LISTCHAR))
  136.             *cp++ = *pathlist++;
  137.         strcpy(cp, name);
  138.         i = openfile(path);
  139.         if ((i < 0) && extension) {
  140.             strcat(path, extension);
  141.             i = openfile(path);
  142.         }
  143.     } while ((i < 0) && *pathlist);
  144.  
  145.     /* examine what our search produced */
  146.     if (i < 0 || cip->i_fp == NULL) {
  147.         /* cannot find a file to open */
  148.         return -1;
  149.     }
  150.     /*
  151.      AMIGA: use stat rather than fstat, since it's not in the Lattice
  152.             C library.
  153.   */
  154.   if (stat(path,&statbuf) < 0) {
  155.         /* unable to stat the open file */
  156.         return -1;
  157.     }
  158.  
  159.     /* note if we will reopen a file and if that is allowed */
  160.     if (rd_once == TRUE && isinoderead(&statbuf) >= 0) {
  161.         /* file is in readset and reopen is false */
  162.         closeinput();
  163.         return 1;
  164.     }
  165.  
  166.     /* add this name to the readset */
  167.     if (addreadset(name, path, &statbuf) < 0) {
  168.         /* cannot add to readset */
  169.         closeinput();
  170.         return -1;
  171.     }
  172.  
  173.     /* file was added to/updated in readset */
  174.     return 0;
  175. }
  176.  
  177. /*
  178.  * Setup for reading from a input file.
  179.  * Returns -1 if file could not be opened.
  180.  */
  181. static int
  182. openfile(name)
  183.     char *name;        /* file name to be read */
  184. {
  185.     FILE *fp;        /* open file descriptor */
  186.  
  187.     if (depth >= DEPTH)
  188.          return -1;
  189.     fp = fopen(name, "r");
  190.     if (fp == NULL)
  191.          return -1;
  192.     cip++;
  193.     cip->i_state = IS_READ;
  194.     cip->i_char = '\0';
  195.     cip->i_str = NULL;
  196.     cip->i_origstr = NULL;
  197.     cip->i_ttystr = NULL;
  198.     cip->i_fp = fp;
  199.     cip->i_line = 1;
  200.     cip->i_name = (char *)malloc(strlen(name) + 1);
  201.     strcpy(cip->i_name, name);
  202.     depth++;
  203.     return 0;
  204. }
  205.  
  206.  
  207. /*
  208.  * Open a string for scanning. String is ended by a null character.
  209.  * String is copied into local memory so it can be trashed afterwards.
  210.  * Returns -1 if cannot open string.
  211.  */
  212. int
  213. openstring(str)
  214.     char *str;        /* string to be opened */
  215. {
  216.     char *cp;        /* copied string */
  217.  
  218.     if ((depth >= DEPTH) || (str == NULL))
  219.          return -1;
  220.     cp = (char *)malloc(strlen(str) + 1);
  221.     if (cp == NULL)
  222.          return -1;
  223.     strcpy(cp, str);
  224.     cip++;
  225.     cip->i_state = IS_READ;
  226.     cip->i_char = '\0';
  227.     cip->i_str = cp;
  228.     cip->i_origstr = cp;
  229.     cip->i_fp = NULL;
  230.     cip->i_name = NULL;
  231.     cip->i_ttystr = NULL;
  232.     cip->i_line = 1;
  233.     depth++;
  234.     return 0;
  235. }
  236.  
  237.  
  238. /*
  239.  * Set to read input from the terminal.
  240.  * Returns -1 if there is no more depth for input.
  241.  */
  242. int
  243. openterminal()
  244. {
  245.     if (depth >= DEPTH)
  246.          return -1;
  247.     cip++;
  248.     cip->i_state = IS_READ;
  249.     cip->i_char = '\0';
  250.     cip->i_str = NULL;
  251.     cip->i_origstr = NULL;
  252.     cip->i_ttystr = NULL;
  253.     cip->i_fp = NULL;
  254.     cip->i_name = NULL;
  255.     cip->i_line = 1;
  256.     depth++;
  257.     return 0;
  258. }
  259.  
  260.  
  261. /*
  262.  * Close the current input source.
  263.  */
  264. static void
  265. closeinput()
  266. {
  267.     if (depth <= 0)
  268.         return;
  269.     if (cip->i_origstr)
  270.         free(cip->i_origstr);
  271.     if (cip->i_fp)
  272.         fclose(cip->i_fp);
  273.     if (cip->i_name)
  274.         free(cip->i_name);
  275.     cip--;
  276.     depth--;
  277. }
  278.  
  279.  
  280. /*
  281.  * Reset the input sources back to the initial state.
  282.  */
  283. void
  284. resetinput()
  285. {
  286.     while (depth > 0)
  287.         closeinput();
  288.     cip = inputs;
  289.     noprompt = FALSE;
  290. }
  291.  
  292.  
  293. /*
  294.  * Set the prompt for terminal input.
  295.  */
  296. void
  297. setprompt(str)
  298.     char *str;
  299. {
  300.     prompt = str;
  301.     noprompt = FALSE;
  302. }
  303.  
  304.  
  305. /*
  306.  * Read the next character from the current input source.
  307.  * End of file closes current input source, and returns EOF character.
  308.  */
  309. int
  310. nextchar()
  311. {
  312.     int ch;            /* current input character */
  313.  
  314.     if (depth == 0)        /* input finished */
  315.          return EOF;
  316.     if (cip->i_state == IS_REREAD) {    /* rereading current char */
  317.          ch = cip->i_char;
  318.          cip->i_state = IS_READ;
  319.          if (ch == '\n')
  320.             cip->i_line++;
  321.          return ch;
  322.     }
  323.     if (cip->i_str) {        /* from string */
  324.         ch = chartoint(*cip->i_str++);
  325.         if (ch == '\0')
  326.             ch = EOF;
  327.     } else if (cip->i_fp) {        /* from file */
  328.         ch = fgetc(cip->i_fp);
  329.     } else if (!stdin_tty) {        /* from file */
  330.         ch = fgetc(stdin);
  331.     } else {            /* from terminal */
  332.         ch = ttychar();
  333.     }
  334.     if (ch == EOF) {        /* fix up end of file */
  335.         closeinput();
  336.         ch = EOF;
  337.     }
  338.     if (depth > 0)
  339.         cip->i_char = ch;    /* save for rereads */
  340.     if (ch == '\n')
  341.         cip->i_line++;
  342.     return ch;
  343. }
  344.  
  345.  
  346. /*
  347.  * Read in the next line of input from the current input source.
  348.  * The line is terminated with a null character, and does not contain
  349.  * the final newline character.  The returned string is only valid
  350.  * until the next such call, and so must be copied if necessary.
  351.  * Returns NULL on end of file.
  352.  */
  353. char *
  354. nextline()
  355. {
  356.     char *cp;
  357.     int ch;
  358.     int len;
  359.  
  360.     cp = linebuf;
  361.     if (linesize == 0) {
  362.         cp = (char *)malloc(TTYSIZE + 1);
  363.         if (cp == NULL)
  364.             math_error("Cannot allocate line buffer");
  365.         linebuf = cp;
  366.         linesize = TTYSIZE;
  367.     }
  368.     len = 0;
  369.     for (;;) {
  370.         noprompt = TRUE;
  371.         ch = nextchar();
  372.         noprompt = FALSE;
  373.         if (ch == EOF)
  374.             return NULL;
  375.         if (ch == '\0')
  376.             continue;
  377.         if (ch == '\n')
  378.             break;
  379.         if (len >= linesize) {
  380.             cp = (char *)realloc(cp, linesize + TTYSIZE + 1);
  381.             if (cp == NULL)
  382.                 math_error("Cannot realloc line buffer");
  383.             linebuf = cp;
  384.             linesize += TTYSIZE;
  385.         }
  386.         cp[len++] = (char)ch;
  387.     }
  388.     cp[len] = '\0';
  389.     return linebuf;
  390. }
  391.  
  392.  
  393. /*
  394.  * Read the next character from the terminal.
  395.  */
  396. static int
  397. ttychar()
  398. {
  399.     int ch;            /* current char */
  400.     int len;        /* length of current command */
  401.     static char charbuf[1024];
  402.  
  403.     /*
  404.      * If we have more to read from the saved command line, then do that.
  405.      * When we see a newline character, then clear the pointer so we will
  406.      * read a new line on the next call.
  407.      */
  408.     if (cip->i_ttystr) {
  409.         ch = chartoint(*cip->i_ttystr++);
  410.         if (ch == '\n')
  411.             cip->i_ttystr = NULL;
  412.         return ch;
  413.     }
  414.  
  415.     /*
  416.      * We need another complete line.
  417.      */
  418.     abortlevel = 0;
  419.     inputwait = TRUE;
  420.     len = getline(noprompt ? "" : prompt, charbuf, sizeof(charbuf));
  421.     if (len == 0) {
  422.         inputwait = FALSE;
  423.         return EOF;
  424.     }
  425.     inputwait = FALSE;
  426.  
  427.     /*
  428.      * Handle shell escape if present
  429.      */
  430.     if (charbuf[0] == '!') {        /* do a shell command */
  431.         char *cmd;
  432.  
  433.         cmd = charbuf + 1;
  434.         system(cmd);
  435.         return '\n';
  436.     }
  437.  
  438.     /*
  439.      * Return the first character of the line, and set up to
  440.      * return the rest of it with later calls.
  441.      */
  442.     ch = chartoint(charbuf[0]);
  443.     if (ch != '\n')
  444.         cip->i_ttystr = charbuf + 1;
  445.     return ch;
  446. }
  447.  
  448.  
  449. /*
  450.  * Return whether or not the input source is the terminal.
  451.  */
  452. BOOL
  453. inputisterminal()
  454. {
  455.     return ((depth <= 0) || ((cip->i_str == NULL) && (cip->i_fp == NULL)));
  456. }
  457.  
  458.  
  459. /*
  460.  * Return the name of the current input file.
  461.  * Returns NULL for terminal or strings.
  462.  */
  463. char *
  464. inputname()
  465. {
  466.     if (depth <= 0)
  467.         return NULL;
  468.     return cip->i_name;
  469. }
  470.  
  471.  
  472. /*
  473.  * Return the current line number.
  474.  */
  475. long
  476. linenumber()
  477. {
  478.     if (depth > 0)
  479.         return cip->i_line;
  480.     return 1;
  481. }
  482.  
  483.  
  484. /*
  485.  * Restore the next character to be read again on the next nextchar call.
  486.  */
  487. void
  488. reread()
  489. {
  490.     if ((depth <= 0) || (cip->i_state == IS_REREAD))
  491.         return;
  492.     cip->i_state = IS_REREAD;
  493.     if (cip->i_char == '\n')
  494.         cip->i_line--;
  495. }
  496.  
  497.  
  498. /*
  499.  * Process all startup files found in the $CALCRC path.
  500.  */
  501. void
  502. runrcfiles()
  503. {
  504.     char path[PATHSIZE+1];    /* name being searched for */
  505.     char *cp;
  506.     char *newcp;
  507.     char *p;
  508.     int i;
  509.  
  510.     /* execute each file in the list */
  511.     for (cp=calcrc, newcp=(char *)strchr(calcrc, LISTCHAR);
  512.          cp != NULL && *cp;
  513.          cp = newcp, 
  514.          newcp=(newcp) ? (char *)strchr(newcp+1, LISTCHAR) : NULL) {
  515.  
  516.         /* load file name into the path */
  517.         if (newcp == NULL) {
  518.             strcpy(path, cp);
  519.         } else {
  520.             strncpy(path, cp, newcp-cp);
  521.             path[newcp-cp] = '\0';
  522.         }
  523.  
  524.         /* find the start of the path */
  525.         /* Bug fixed. This comparison checked for ':', not LISTCHAR */
  526.     p = (path[0] == LISTCHAR) ? path+1 : path;
  527.         if (p[0] == '\0') {
  528.             continue;
  529.         }
  530.  
  531.         /* process the current file in the list */
  532.         i = openfile(p);
  533.         if (i < 0)
  534.             continue;
  535.         getcommands(FALSE);
  536.     }
  537. }
  538.  
  539.  
  540. /*
  541.  * isinoderead - determine if we have read a given dev/inode
  542.  *
  543.  * This function returns the index of the readset element that matches
  544.  * a given device/inode, -1 otherwise.
  545.  */
  546. static int
  547. isinoderead(sbuf)
  548.     struct stat *sbuf;        /* stat of the inode in question */
  549. {
  550.     int i;
  551.  
  552.     /* deal with the empty case */
  553.     if (readset == NULL || maxreadset <= 0) {
  554.         /* readset is empty */
  555.         return -1;
  556.     }
  557.  
  558.     /* scan the entire readset */
  559.     for (i=0; i < maxreadset; ++i) { 
  560.         if (readset[i].active && 
  561.             sbuf->st_dev == readset[i].inode.st_dev &&
  562.             sbuf->st_ino == readset[i].inode.st_ino) {
  563.             /* found a match */
  564.             return i;
  565.         }
  566.     }
  567.  
  568.     /* no match found */
  569.     return -1;
  570. }
  571.  
  572.  
  573. /*
  574.  * findfreeread - find the next free readset element
  575.  *
  576.  * This function will return the index of the next free readset element.
  577.  * If needed, this function will allocate new readset elements.
  578.  *
  579.  * This function returns the index of the next free element, or -1.
  580.  */
  581. static int
  582. findfreeread()
  583. {
  584.     int i;
  585.  
  586.     /* deal with an empty readset case */
  587.     if (readset == NULL || maxreadset <= 0) {
  588.  
  589.         /* malloc a new readset */
  590.         readset = (READSET *)malloc((READSET_ALLOC+1)*sizeof(READSET));
  591.         if (readset == NULL) {
  592.             return -1;
  593.         }
  594.         maxreadset = READSET_ALLOC;
  595.         for (i=0; i < READSET_ALLOC; ++i) {
  596.             readset[i].active = 0;
  597.         }
  598.  
  599.         /* return first entry */
  600.         return 0;
  601.     }
  602.  
  603.     /* try to find a free readset entry */
  604.     for (i=0; i < maxreadset; ++i) {
  605.         if (readset[i].active == 0) {
  606.             /* found a free readset entry */
  607.             return i;
  608.         }
  609.     }
  610.  
  611.     /* all readset entries are in use, allocate more */
  612.     readset = (READSET *)realloc(readset, 
  613.         (maxreadset+READSET_ALLOC) * sizeof(READSET));
  614.     if (readset == NULL) {
  615.         return -1;
  616.     }
  617.     for (i=0; i < READSET_ALLOC; ++i) {
  618.         readset[i+maxreadset].active = 0;
  619.     }
  620.     maxreadset += READSET_ALLOC;
  621.  
  622.     /* return the furst newly allocated free entry */
  623.     return maxreadset-READSET_ALLOC;
  624. }
  625.  
  626.  
  627. /*
  628.  * addreadset - add a entry to the readset array if it is not already there
  629.  *
  630.  * This function attempts to add a file into the readset.  If the readset
  631.  * has an entry with a matching dev/inode, then that entry is updated with
  632.  * the new name and path.  If no such readset entry is found, a new entry
  633.  * is added.
  634.  *
  635.  * This function returns the index of the readset entry, or -1 if error.
  636.  */
  637. static int
  638. addreadset(name, path, sbuf)
  639.     char *name;    /* name given to read or include */
  640.     char *path;    /* full pathname of file */
  641.     struct stat *sbuf;    /* stat of the path */
  642. {
  643.     int ret;        /* index to return */
  644.  
  645.     /* find the inode */
  646.     ret = isinoderead(sbuf);
  647.     if (ret < 0) {
  648.         /* not in readset, find a free node */
  649.         ret = findfreeread();
  650.         if (ret < 0) {
  651.             /* cannot find/form a free readset entry */
  652.             return -1;
  653.         }
  654.     } else {
  655.         /* found an readset entry, free old readset data */
  656.         if (readset[ret].name != NULL) {
  657.             free(readset[ret].name);
  658.         }
  659.         if (readset[ret].path != NULL) {
  660.             free(readset[ret].path);
  661.         }
  662.     }
  663.  
  664.     /* load our information into the readset entry */
  665.     readset[ret].name = (char *)malloc(strlen(name)+1);
  666.     if (readset[ret].name == NULL) {
  667.         return -1;
  668.     }
  669.     strcpy(readset[ret].name, name);
  670.     readset[ret].path = (char *)malloc(strlen(path)+1);
  671.     if (readset[ret].path == NULL) {
  672.         return -1;
  673.     }
  674.     strcpy(readset[ret].path, path);
  675.     readset[ret].inode = *sbuf;
  676.     readset[ret].active = 1;
  677.  
  678.     /* return index of the newly added entry */
  679.     return ret;
  680. }
  681.  
  682.  
  683. /* END CODE */
  684.